package com.laoxu.java.multithread;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 *
 *  调用wait()方法后，线程进入等待状态，wait()方法不会返回，直到将来某个时刻，线程从等待状态被其他线程唤醒后，wait()方法才会返回，然后，继续执行下一条语句。
 *  notify：让等待的线程被重新唤醒，然后从wait()方法返回
 *      notifyAll()将唤醒所有当前正在this锁等待的线程，而notify()只会唤醒其中一个（具体哪个依赖操作系统，有一定的随机性）。
 *      这是因为可能有多个线程正在getTask()方法内部的wait()中等待，使用notifyAll()将一次性全部唤醒。
 *      通常来说，notifyAll()更安全。有些时候，如果我们的代码逻辑考虑不周，用notify()会导致只唤醒了一个线程，而其他线程可能永远等待下去醒不过来了
 * 实现：
 *
 * 线程1可以调用addTask()不断往队列中添加任务；
 * 线程2可以调用getTask()从队列中获取任务。如果队列为空，则getTask()应该等待，直到队列中至少有一个任务时再返回
 *
 */
public class WaitAndNotifyExample {
    public static void main(String[] args) throws InterruptedException{
        TaskQueue q = new TaskQueue();
        List<Thread> ts = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Thread getThread = new Thread(() -> {
                // 执行task
                while (true){
                    try {
                        String s = q.getTask();
                        System.out.println(Thread.currentThread().getName()+"======执行任务： "+s);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
            });

            getThread.start();
            ts.add(getThread);
        }

        Thread addThread = new Thread(()->{
            for (int i = 0; i < 10; i++) {
                // 生产task
                String s = "t-"+Math.random();
                System.out.println(Thread.currentThread().getName()+"=======分配task： "+ s);
                q.addTask(s);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        addThread.start();
        addThread.join();
        Thread.sleep(100);
        for (Thread t: ts) {
            t.interrupt();
        }

    }
}

class TaskQueue {
    Queue<String> queue = new LinkedList<>();

    public synchronized void addTask(String s) {
        this.queue.add(s);
        this.notifyAll();
    }

    public synchronized String getTask() throws InterruptedException {
        while (queue.isEmpty()) {
            this.wait();
        }
        return queue.remove();
    }
}